package com.azxc.rapid.auth.endpoint;

import com.azxc.rapid.auth.bean.TokenRefreshParam;
import com.azxc.rapid.auth.bean.UserLoginParam;
import com.azxc.rapid.auth.utils.CheckSign;
import com.azxc.rapid.common.cache.CacheNames;
import com.azxc.rapid.core.cache.utils.CacheUtil;
import com.azxc.rapid.core.jwt.JwtUtil;
import com.azxc.rapid.core.jwt.props.JwtProperties;
import com.azxc.rapid.core.launch.constant.TokenConstant;
import com.azxc.rapid.core.log.annotation.ApiLog;
import com.azxc.rapid.core.redis.cache.RapidRedis;
import com.azxc.rapid.core.secure.RapidUser;
import com.azxc.rapid.core.secure.utils.AuthUtil;
import com.azxc.rapid.core.tenant.annotation.NonDS;
import com.azxc.rapid.core.tool.api.R;
import com.azxc.rapid.core.tool.support.Kv;
import com.azxc.rapid.core.tool.utils.StringUtil;
import com.azxc.rapid.core.tool.utils.WebUtil;
import com.azxc.rapid.supervise.feign.ISuperviseClient;
import com.azxc.rapid.system.entity.Dept;
import com.azxc.rapid.system.feign.ISysClient;
import com.azxc.rapid.system.user.feign.IUserClient;
import com.wf.captcha.SpecCaptcha;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.time.Duration;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import static com.azxc.rapid.auth.granter.SSoTokenGranter.SECRET_KEY;
import static com.azxc.rapid.core.cache.constant.CacheConstant.*;

/**
 * RapidEndPoint
 *
 *
 */
@NonDS
@Slf4j
@RestController
public class RapidTokenEndPoint {
	@Value("${server.port}")
	private Long port;
	@Autowired
	private RestTemplate restTemplate;
	@Autowired
	private RapidRedis rapidRedis;
	@Autowired
	private JwtProperties jwtProperties;

	@Autowired
	private ISysClient sysClient;

	private static final String ONLINE_KEY = "online:";

	@GetMapping("/oauth/user-info")
	public R<Authentication> currentUser(Authentication authentication) {
		return R.data(authentication);
	}

	@GetMapping("/oauth/captcha")
	public Kv captcha() {
		SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 5);
		String verCode = specCaptcha.text().toLowerCase();
		String key = StringUtil.randomUUID();
		// 存入redis并设置过期时间为30分钟
		rapidRedis.setEx(CacheNames.CAPTCHA_KEY + key, verCode, Duration.ofMinutes(30));
		// 将key和base64返回给前端
		return Kv.create().set("key", key).set("image", specCaptcha.toBase64());
	}

	@ApiLog("退出登录")
	@GetMapping("/oauth/logout")
	public Kv logout() {
		RapidUser user = AuthUtil.getUser();
		if (user != null && jwtProperties.getState()) {
			String token = JwtUtil.getToken(WebUtil.getRequest().getHeader(TokenConstant.HEADER));
			JwtUtil.removeAccessToken(user.getTenantId(), String.valueOf(user.getUserId()), token);
			rapidRedis.del(ONLINE_KEY + user.getUserName());
		}
		// （接收到推送消息的话，可以在登录时把uid和token保存到redis，需要的时候从里面获取，然后再调用推出接口。）调用统一认证接口，通知推出。
		return Kv.create().set("success", "true").set("msg", "success");
	}

	@GetMapping("/oauth/clear-cache")
	public  Kv clearCache() {
		CacheUtil.clear(BIZ_CACHE);
		CacheUtil.clear(USER_CACHE);
		CacheUtil.clear(DICT_CACHE);
		CacheUtil.clear(FLOW_CACHE);
		CacheUtil.clear(SYS_CACHE);
		CacheUtil.clear(PARAM_CACHE);
		CacheUtil.clear(RESOURCE_CACHE);
		CacheUtil.clear(MENU_CACHE);
		CacheUtil.clear(DICT_CACHE, Boolean.FALSE);
		CacheUtil.clear(MENU_CACHE, Boolean.FALSE);
		CacheUtil.clear(PARAM_CACHE, Boolean.FALSE);
		return Kv.create().set("success", "true").set("msg", "success");
	}

	@SneakyThrows
	@ApiLog("登录用户验证")
	@GetMapping("/auth/login")
	public R<Object> login(@Valid UserLoginParam userLoginParam, @RequestHeader HttpHeaders httpHeaders)  {
		//授权请求信息

		MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
		HttpEntity httpEntity = new HttpEntity(map,httpHeaders);
		StringBuffer urlBuffer = new StringBuffer();
		String baseUrl = "http://127.0.0.1:"+port+"/oauth/token";
		urlBuffer.append(baseUrl).append("?")
			.append("tenantId=").append(userLoginParam.getTenantId()).append("&")
			.append("username=").append(userLoginParam.getUsername()).append("&")
			.append("password=").append(userLoginParam.getPassword()).append("&")
			.append("grant_type=").append(userLoginParam.getGrant_type()).append("&")
			.append("scope=").append(userLoginParam.getScope()).append("&")
			.append("type=").append(userLoginParam.getType()).append("&")
			.append("code=").append(userLoginParam.getCode()).append("&")
			.append("oauthCode=").append(userLoginParam.getOauthCode());
		//获取 用户登录信息userDetail
		OAuth2AccessToken auth2AccessToken = null;
		try{
			ResponseEntity<OAuth2AccessToken> result = restTemplate.exchange(urlBuffer.toString(), HttpMethod.POST, httpEntity, OAuth2AccessToken.class);
			auth2AccessToken = result.getBody();
			//检查用户是否在线
			if(rapidRedis.exists(ONLINE_KEY + userLoginParam.getUsername())){
				//获取到前一次登录的access_token
				String access_token = rapidRedis.get(ONLINE_KEY + userLoginParam.getUsername());
				//移除旧的access_token，持有此access_token的访问失效
				JwtUtil.removeAccessToken(userLoginParam.getTenantId(), (String)auth2AccessToken.getAdditionalInformation().get("user_id"), access_token );
			}
			//设置最新的access_token
			JwtUtil.addAccessToken(userLoginParam.getTenantId(), (String)auth2AccessToken.getAdditionalInformation().get("user_id"),auth2AccessToken.getValue(),3600);
			//设置在线时间为token得有效时间
			rapidRedis.setEx(ONLINE_KEY + userLoginParam.getUsername(),auth2AccessToken.getValue(), Long.valueOf(result.getBody().getExpiresIn()));

		}catch (Exception e){
			return R.fail(e.getMessage()) ;
		}
		if(auth2AccessToken.getAdditionalInformation()!=null&&auth2AccessToken.getAdditionalInformation().get("dept_id")!=null){
			Dept dept=sysClient.getDept(Long.parseLong(auth2AccessToken.getAdditionalInformation().get("dept_id").toString())).getData();
			auth2AccessToken.getAdditionalInformation().put("regionCode",dept.getRegionCode());
		}

		return R.data(auth2AccessToken) ;
	}

	@GetMapping("/auth/refresh_token")
	public R<Object> refreshToken(@Valid TokenRefreshParam tokenRefreshParam, @RequestHeader HttpHeaders httpHeaders)  {
		//授权请求信息
		MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
		HttpEntity httpEntity = new HttpEntity(map,httpHeaders);
		StringBuffer urlBuffer = new StringBuffer();
		String baseUrl = "http://127.0.0.1:"+port+"/oauth/token";
		urlBuffer.append(baseUrl).append("?")
			.append("tenantId=").append(tokenRefreshParam.getTenantId()).append("&")
			.append("grant_type=").append(tokenRefreshParam.getGrant_type()).append("&")
			.append("scope=").append(tokenRefreshParam.getScope()).append("&")
			.append("refresh_token=").append(tokenRefreshParam.getRefresh_token());

		//获取 用户登录信息userDetail
		OAuth2AccessToken auth2AccessToken = null;
		try{
			ResponseEntity<OAuth2AccessToken> result = restTemplate.exchange(urlBuffer.toString(), HttpMethod.POST, httpEntity, OAuth2AccessToken.class);
			auth2AccessToken = result.getBody();
			//设置在线时间为token得有效时间
			String uerName = (String)auth2AccessToken.getAdditionalInformation().get("user_name");
			rapidRedis.setEx(ONLINE_KEY + uerName,auth2AccessToken.getValue(), Long.valueOf(result.getBody().getExpiresIn()));
		}catch (Exception e){
			return R.fail(e.getMessage()) ;
		}
		if(auth2AccessToken.getAdditionalInformation()!=null&&auth2AccessToken.getAdditionalInformation().get("dept_id")!=null){
			Dept dept=sysClient.getDept(Long.parseLong(auth2AccessToken.getAdditionalInformation().get("dept_id").toString())).getData();
			auth2AccessToken.getAdditionalInformation().put("regionCode",dept.getRegionCode());
		}
		return R.data(auth2AccessToken) ;
	}


	//存储cookie，跳转到首页
	@GetMapping("/oauth/sso/redirect")
	public ModelAndView redirect(HttpServletRequest request, HttpServletResponse response) {
		//解析签名是否正确
		Map<String,String> params=new HashMap();
		Enumeration enu=request.getParameterNames();
		while(enu.hasMoreElements()){
			String paraName=(String)enu.nextElement();
			params.put(paraName,request.getParameter(paraName));
		}
		log.error("params redirectUrl:"+params.get("redirectUrl"));
		params.remove("redirectUrl");
		params.remove("sign");
		String sign=request.getParameter("sign");
		if(StringUtil.isEmpty(sign)){
			throw new InvalidGrantException("无签名");
		}
		boolean check=CheckSign.checkSign(params,sign,SECRET_KEY);
		if(!check){
			throw new InvalidGrantException("签名校验失败");
		}
		String redirectUrl=request.getParameter("redirectUrl");
		//log.error("redirectUrl:"+redirectUrl);
		if(StringUtil.isEmpty(redirectUrl)){
			throw new InvalidGrantException("无重定向url");
		}
		//回调地址写死
		redirectUrl="http://8.130.161.168:81/#/wel/index";
		String accessTokenName = "";
		String refreshTokenName = "";
		String accessToken ="";
		String refreshToken = "";

		accessTokenName = request.getParameter("accessTokenName");
		refreshTokenName = request.getParameter("refreshTokenName");
		accessToken = request.getParameter(accessTokenName);
		refreshToken = request.getParameter(refreshTokenName);
		/*System.out.println("accessTokenName:"+accessTokenName);
		System.out.println("refreshTokenName:"+refreshTokenName);
		System.out.println("accessToken:"+accessToken);
		System.out.println("refreshToken:"+refreshToken);*/
		if(StringUtil.isEmpty(accessTokenName)||StringUtil.isEmpty(refreshTokenName)||StringUtil.isEmpty(accessToken)
			||StringUtil.isEmpty(refreshToken)){
			throw new InvalidGrantException("缺少accessToken或refreshToken信息");
		}

		//创建Cookie
		Cookie cookie = new Cookie(accessTokenName, accessToken);
		cookie.setPath("/");

		Cookie cookie2 = new Cookie(refreshTokenName, refreshToken);
		cookie2.setPath("/");
		//设置Cookie的最大生命周期,否则浏览器关闭后Cookie即失效
		//cookie.setMaxAge(Integer.MAX_VALUE);
		//将Cookie加到response中
		response.addCookie(cookie);
		response.addCookie(cookie2);
		return new ModelAndView(new RedirectView(redirectUrl));
	}

	@SneakyThrows
	@ApiLog("登录用户验证")
		@GetMapping("/auth/otherLogin")
	public R<Object> otherLogin(UserLoginParam userLoginParam, @RequestHeader HttpHeaders httpHeaders)  {
		//授权请求信息
		MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
		HttpEntity httpEntity = new HttpEntity(map,httpHeaders);
		StringBuffer urlBuffer = new StringBuffer();
		String baseUrl = "http://127.0.0.1:"+port+"/oauth/token";
		urlBuffer.append(baseUrl).append("?")
			.append("tenantId=").append(userLoginParam.getTenantId()).append("&")
			.append("grant_type=").append(userLoginParam.getGrant_type()).append("&")
			.append("scope=").append(userLoginParam.getScope()).append("&")
			.append("code=").append(userLoginParam.getCode()).append("&")
			.append("account=").append(userLoginParam.getAccount()).append("&")
			.append("oauthCode=").append(userLoginParam.getOauthCode());
		log.info("baseUrl:"+baseUrl);
		//获取 用户登录信息userDetail
		OAuth2AccessToken auth2AccessToken = null;
		try{
			ResponseEntity<OAuth2AccessToken> result = restTemplate.exchange(urlBuffer.toString(), HttpMethod.POST, httpEntity, OAuth2AccessToken.class);
			auth2AccessToken = result.getBody();
			//检查用户是否在线
			if(rapidRedis.exists(ONLINE_KEY + userLoginParam.getUsername())){
				//获取到前一次登录的access_token
				String access_token = rapidRedis.get(ONLINE_KEY + userLoginParam.getUsername());
				//移除旧的access_token，持有此access_token的访问失效
				JwtUtil.removeAccessToken(userLoginParam.getTenantId(), (String)auth2AccessToken.getAdditionalInformation().get("user_id"), access_token );
			}
			if(userLoginParam.getGrant_type().equals("portal")&&null==userLoginParam.getTenantId()){
				userLoginParam.setTenantId("000000");
				if(StringUtil.isEmpty(userLoginParam.getUsername())&&null!=auth2AccessToken.getAdditionalInformation().get("user_name")){
					userLoginParam.setUsername(auth2AccessToken.getAdditionalInformation().get("user_name").toString());
				}
			}
			//设置最新的access_token
			JwtUtil.addAccessToken(userLoginParam.getTenantId(), (String)auth2AccessToken.getAdditionalInformation().get("user_id"),auth2AccessToken.getValue(),3600);
			//设置在线时间为token得有效时间
			rapidRedis.setEx(ONLINE_KEY + userLoginParam.getUsername(),auth2AccessToken.getValue(), Long.valueOf(result.getBody().getExpiresIn()));

		}catch (Exception e){
			return R.fail(e.getMessage()) ;
		}
		return R.data(auth2AccessToken) ;
	}
}
